#ifndef GTL_ENGINE_PROPERTY_HEADER_GUARD
#define GTL_ENGINE_PROPERTY_HEADER_GUARD

#include <GTL/gtlAutomember.h>
#include <GTL/gtlDebug.h>
#include <GTL/gtlCommon.h>
#include <GTL/gtlCPPUtils.h>
#include <GTL/gtlStructures.h>
//#include <GTLEngine/gtlCore.h>
#include <eastl/string.h>

namespace GTL { namespace Engine {
	class BackendInterface;
	extern BackendInterface* Backend;

	struct Property;
	struct Modifier
	{
		int Type;
		void (*InitFunction)(Modifier*,Property*);

		float FloatValues[4];
		int IntValues[4];
		const char* StringValues[4];

		static void Init(Modifier*, Property*) {}
		Modifier(int type) : Type(type), InitFunction(&Init) {}
	};

	struct Property : public GTL::AutoMembers::AutoMember
	{
		virtual int GetType() { return 'prop'; }

		Modifier* Modifiers;
		int ModifierCount;

		/*
		enum
		{
			ReadOnly =						1<<0,
			WriteOnly =						1<<1,
			Published =						1<<2,
			Replicated =					1<<3,
			Transient =						1<<4,
			AlertOnChange =       1<<5,
		};
		GTL::Utilities::Flags Flags;*/
		GTL_FLAGS(Flags)
		{
			ReadOnly,
			WriteOnly,
			Published,
			Replicated,
			Transient,
			AlertOnChange,
		};

		struct Event
		{
			enum {
				PropertyEvent = 'PROE',
				Changed,
			};
		};

		void UpdateFlags();
		const Modifier* GetModifier(int id) const;

		virtual void Reset() = 0;
		virtual const char* GetName() const = 0;
		virtual const char* GetTypeName() const = 0;
		virtual void MarkClean() = 0;
		virtual bool SetFromNode(const GTL::Structures::DataNode* node) = 0;
	};

	namespace Is
	{
		/// REMEMBER! Modifier CANNOT have members!
		struct NoOp : public Modifier
		{
			NoOp() : Modifier('noop') {}
		};
		struct ReadOnly : public Modifier
		{
			static void Init(Modifier* mod, Property* bp);
			ReadOnly() : Modifier('read') { InitFunction = &ReadOnly::Init; }
		};
		struct InGroup : public Modifier
		{
			InGroup(const char* name) : Modifier('grou') { StringValues[0] = name; }
		};

		struct AlertedOnChange : public Modifier
		{
			static void Init(Modifier* mod, Property* bp);
			AlertedOnChange(int ev) : Modifier('aler') { IntValues[0] = ev; InitFunction = &AlertedOnChange::Init; }
		};
	}

	void AlertGameObject(void* go, void* sender, int ofwhat);

	template <typename T>
	struct PropertyTemplate : public Property
	{
		PropertyTemplate& Set(const T& other)
		{ 
			if (HasFlag(ReadOnly))
			{
				Backend->LogError("Property %s in class %s is read-only!", GetName(), ParentProducible->GetName());
			} else
			{
				mValue = other;
				mDirty = mPrevValue != other;

				if (HasFlag(AlertOnChange))
				{
					const Modifier* alertMod = GetModifier('aler');
					AlertGameObject(ParentProducible, this, alertMod->IntValues[0]);
				}
			}
			return *this;
		}

		virtual void MarkClean()
		{
			mPrevValue = mValue;
			mDirty = false;
		}

		virtual bool SetFromNode(const GTL::Structures::DataNode* node)
		{
			return NodeToObject(mValue, node);
		}

		template <typename FORMAT>
		eastl::string GetRepresentation()
		{
			return FORMAT::GetRepresentation<T>(mValue);
		}

		virtual T GetDefaultValue() const = 0;
	protected:
		PropertyTemplate(const T& value) : mValue(value), mPrevValue(value), mDirty(false) {}
		bool mDirty;
		T mValue;
		T mPrevValue;
	};

	/// FIXME: This could be a source of errors - the static Modifier array can cause pre-initialization errors
	/// when two threads are running this code; Technically `modifiers` could be thread-local, but it's created 
	/// a LOT of times (once per property), so that sucks
	/// TODO: Moze w przyszlosci zeby Modifiers'y nie byly wskaznikiem w property tylko funkcja wirtualna ktora zwraca konkrenty modifier
#define property(type, name, value, ...) \
	struct __property_##name : public GTL::Engine::PropertyTemplate<type> \
	{ \
		__property_##name() : PropertyTemplate(value) { \
			AddAutoMemberToPostOrderList(); \
			GTL::Common::NOPFunction(&GTL::Common::LValueNOPFunction, (0)); \
			static Modifier modifiers[] = { Is::NoOp(), __VA_ARGS__ }; \
			Modifiers = modifiers; \
			ModifierCount = sizeof(modifiers)/sizeof(Modifier); \
			UpdateFlags(); \
		} \
		virtual void Reset() { \
			Set(value); \
		} \
		virtual type GetDefaultValue() const { \
			return value; \
		} \
		virtual const char* GetName() const { return #name; } \
		virtual const char* GetTypeName() const { return #type; } \
		__property_##name & operator=(const type& other) { Set(other); return *this; } \
		inline const type operator -> () const { return mValue; } \
		inline operator type () { return mValue; } \
		inline operator const type () const { return mValue; } \
		template <class T> type operator * (const T& other) { return mValue * other; } \
		template <class T> type operator + (const T& other) { return mValue + other; } \
		template <class T> __property_##name & operator += (const T& other) { Set(mValue+other); return *this; } \
		template <class T> __property_##name & operator |= (const T& other) { Set(mValue|other); return *this; } \
		template <class T> __property_##name & operator &= (const T& other) { Set(mValue&other); return *this; } \
		template <class T> __property_##name & operator ^= (const T& other) { Set(mValue^other); return *this; } \
		template <class T> bool operator == (const T& other) { return mValue == other; } \
	} name;

#define flags_property(name) enum __flags_##name; \
	property (uint64_t, name##Flags, 0); \
	inline bool HasFlag(__flags_##name flag) const { return !!((uint64_t)name##Flags & (1LL<<flag)); } \
	inline void SetFlag(__flags_##name flag) { name##Flags |= (1LL<<flag); } \
	inline void UnsetFlag(__flags_##name flag) { name##Flags &= ~(1LL<<flag); } \
	inline void ToggleFlag(__flags_##name flag) { name##Flags ^= (1LL<<flag); } \
	inline uint64_t Get##name##FlagsValue() const { return name##Flags; } \
	inline void Set##name##FlagsValue(uint64_t val) { name##Flags = val; } \
	enum __flags_##name
}}

#endif 